<template>
	<div
		:class="[
			'app-editor-container',
			'app-cascader',
			`app-cascader-${name}`,
			`app-cascader--${editorStyle}`,
		]"
	>
		<template v-if="editorStyle == 'tree'">
			<a-tree-select
				:size="size"
				:class="['app-editor-container']"
				treeNodeFilterProp="label"
				v-model:value="selectValue"
				:placeholder="placeholder"
				:allowClear="clearable"
				:tree-data="treeData"
				:load-data="treeLoadData"
				:disabled="disabled || readonly"
				:tree-checkable="multiple"
				:filterTreeNode="filterable"
				:showSearch="filterable"
				:fieldNames="{ children: 'children', label: 'label', value: 'nodeId' }"
				@change="handleTreeValueChange"
			>
				<template #tagRender="{ label, closable, onClose, option }">
					<a-tag
						:class="`app-cascader--${editorStyle}-tag`"
						:closable="closable"
						@close="onClose"
					>
						{{ option.plabel.join(separator) + separator + label }}
					</a-tag>
				</template>
				<template #title="node">
					<!-- active 属性用于判断 是树的title插槽还是select的单选插槽 -->
					<template v-if="node.hasOwnProperty('active')">
						{{ node.label }}
					</template>
					<template v-else>
						{{
							node.plabel
								? node.plabel.join(separator) + separator + node.label
								: node.label
						}}
					</template>
				</template>
			</a-tree-select>
		</template>
		<template v-else>
			<a-cascader
				:class="['app-editor-container']"
				:size="size"
				v-model:value="selectValue"
				:disabled="disabled || readonly"
				:multiple="multiple"
				:load-data="cascaderLoadData"
				:allowClear="clearable"
				:options="treeData"
				:show-search="{ filter }"
				:fieldNames="{ children: 'children', label: 'label', value: 'value' }"
				:placeholder="placeholder"
				showCheckedStrategy="SHOW_CHILD"
				@change="handleCascaderValueChange"
			>
				<template #displayRender="{ labels }">
					<template v-if="!multiple">
						{{ labels ? labels.join(separator) : null }}
					</template>
					<template v-else>
						<template v-for="(label, index) in labels">
							{{ label }}{{ index !== labels.length - 1 ? separator : null }}
						</template>
					</template>
				</template>
			</a-cascader>
		</template>
	</div>
</template>
<script setup lang="ts">
import {
	deepCopy,
	IEntityDetail,
	IEvent,
	IParam,
	typeOf,
	createUUID,
} from '@/core';
import { isFunc } from 'qx-util';
import { Ref } from 'vue';
import { CascaderProps, ValueItemType } from './cascader';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { useEditorTypeParams } from '@/hooks/use-editor';
const props = defineProps(CascaderProps);
interface EditorEmit {
	(name: 'component-action', value: IEvent<string>): void;
}
const emit = defineEmits<EditorEmit>();
// 类型校验
const { validateConfig } = useEditorTypeParams();
validateConfig();
/**
 * 树数据
 */
const treeData: Ref<any[]> = ref([]);
/**
 * 数据集
 */
const items: any[] = [];
/**
 * 选中数据
 */
let selectValue: Ref<any> = ref(props.multiple ? [] : '');
/**
 * 初始化
 */
let init: boolean = false;

watch(
	() => props.value,
	(newVal: any, oldVal: any) => {
		if (!init) {
			init = true;
			if (newVal) {
				handleSelectData();
			}
			loadData(props.valueItems[0]).then((_treeData: IParam[]) => {
				if (_treeData) {
					if (treeData.value.length > 0) {
						_treeData.forEach((node: IParam) => {
							const index = treeData.value.findIndex(
								(_node: any) => node.value == _node.value
							);
							if (index > -1) {
								Object.assign(treeData.value[index], node);
							} else {
								treeData.value.push(node);
							}
						});
					} else {
						treeData.value = _treeData;
					}
				}
			});
		}
	}
);

/**
 * @description 级联选择过滤
 * @param inputValue 输入值
 * @param path 路径
 */
const filter: ShowSearchType['filter'] = (inputValue: string, path: any[]) => {
	return path.some(
		(option: any) =>
			option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
	);
};

/**
 * @description 获取树数据
 * @param nodes  树数据
 * @param parentKey 父数据
 * @param childNodes 子节点数据集合
 */
const getTreeData = (
	nodes: IParam[],
	parentKey: string,
	childNodes: IParam[]
) => {
	if (nodes && nodes.length > 0) {
		nodes.forEach((node: any) => {
			if (node.value == parentKey) {
				childNodes = node.children || [];
			} else {
				getTreeData(node.children, parentKey, childNodes);
			}
		});
	} else {
		childNodes = [];
	}
};

/**
 * @description 设置树数据
 * @param treeData 树数据
 * @param treePath 树路径
 * @param index 树层级索引
 */
const setTreeData = (
	treeData: IParam[],
	treePath: string[],
	index: number,
	parent?: any
) => {
	if (index > -1 && index < treePath.length) {
		const item = items.find((item: IParam) => item.value == treePath[index]);
		const treeItem = treeData.find(
			(item: IParam) => item.value == treePath[index]
		);
		if (!treeItem) {
			const children: IParam = {
				label: item.label,
				value: item.value,
				children: [],
				plabel: index ? [...parent.plabel, parent.label] : [],
				pvalue: index ? [...parent.pvalue, parent.value] : [],
				isLeaf: index == treePath.length - 1,
				nodeId: `${parent ? parent.value + '_' + item.value : item.value}`,
				index,
			};
			setTreeData(children.children, treePath, index + 1, children);
			treeData.push(children);
		} else {
			if (!treeItem.children) {
				treeItem.children = [];
			}
			setTreeData(treeItem.children, treePath, index + 1, treeItem);
		}
	}
};

/**
 * @description 处理选中数据
 */
const handleSelectData = () => {
	if (props.value && props.valueItems.length > 0) {
		const values: string[] = JSON.parse(props.value);
		const selectData: IParam[] = [];
		const valueItemData: IParam[] = [];
		props.valueItems.forEach((valueItem: any) => {
			valueItemData.push({
				name: valueItem.name,
				value: props.data[valueItem.name]
					? props.data[valueItem.name].split(',')
					: [],
			});
		});
		values.forEach((label: string, index: number) => {
			const value: string[] = [];
			valueItemData.forEach((item: IParam) => {
				value.push(item.value[index]);
			});
			selectData.push({ label, value: value.join(props.separator) });
		});
		selectData.forEach((select: IParam) => {
			const _values: any[] = select.value.split(props.separator);
			const _labels: any[] = select.label.split(props.separator);
			_values.forEach((value: string, index: number) => {
				const item = items.find((item: any) => item.value == value);
				if (!item) {
					items.push({ value, label: _labels[index] });
				}
			});
		});
		selectValue.value = [];
		let tempSelectValue: any[] = [];
		let tempTreeSelectValue: any;
		if (!props.multiple) {
			tempTreeSelectValue = '';
			tempSelectValue = selectData[0].value.split(props.separator);
			tempTreeSelectValue =
				tempSelectValue.length > 1
					? tempSelectValue[tempSelectValue.length - 2] +
					  '_' +
					  tempSelectValue[tempSelectValue.length - 1]
					: tempSelectValue[tempSelectValue.length - 1];
			setTreeData(treeData.value, tempSelectValue, 0);
		} else {
			tempTreeSelectValue = [];
			selectData.forEach((select: IParam) => {
				tempSelectValue.push(select.value.split(props.separator));
			});
			tempSelectValue.forEach((select: string[]) => {
				setTreeData(treeData.value, select, 0);
				tempTreeSelectValue.push(
					select.length > 1
						? select[select.length - 2] + '_' + select[select.length - 1]
						: select[select.length - 1]
				);
			});
		}
		selectValue.value = Object.is(props.editorStyle, 'tree')
			? tempTreeSelectValue
			: tempSelectValue;
	}
};

/**
 * @description 处理级联数据改变
 */
const handleCascaderValueChange = () => {
	// 计算值项选中数据
	const valueItemData: any[] = [];
	props.valueItems.forEach((valueItem: any) => {
		valueItemData.push({ name: valueItem.name, value: [] });
	});
	selectValue.value?.forEach((item: any, index: number) => {
		if (!props.multiple) {
			valueItemData[index].value.push(item);
		} else {
			item.forEach((_item: any, _index: number) => {
				valueItemData[_index].value.push(_item);
			});
		}
	});
	// 计算当前选中数据文本
	const curSelectPath: any[] = [];
	let curSelectText: string[] = [];
	selectValue.value?.forEach((selected: any) => {
		if (!props.multiple) {
			const selectItem = items.find((item: any) => item.value == selected);
			if (selectItem) {
				curSelectPath.push(selectItem.label);
			}
		} else {
			const selectItems: string[] = selected.map((select: string) => {
				const selectItem = items.find((item: any) => item.value == select);
				if (selectItem) {
					return selectItem.label;
				}
			});
			curSelectPath.push(selectItems);
		}
	});
	valueItemData.forEach((valueItem: any) => {
		emit('component-action', {
			name: valueItem.name,
			action: 'valueChange',
			data: valueItem.value.length > 0 ? valueItem.value.join(',') : null,
		});
	});
	if (curSelectPath.length > 0) {
		if (props.multiple) {
			curSelectPath.forEach((path: string[]) => {
				curSelectText.push(path.join(props.separator));
			});
		} else {
			curSelectText = [curSelectPath.join(props.separator)];
		}
	}
	emit('component-action', {
		name: props.name as string,
		action: 'valueChange',
		data: curSelectText.length > 0 ? JSON.stringify(curSelectText) : null,
	});
};

/**
 * @description 处理树选中改变
 * @param curValue 选中值
 * @param curLabel 选中文本
 * @param extra 选中参数
 */
const handleTreeValueChange = (
	curValue: string | string[],
	curLabel: string | string[],
	extra: IParam
) => {
	const valueItemData: any[] = [];
	let curSelectText: string[] = [];
	props.valueItems.forEach((valueItem: any) => {
		valueItemData.push({ name: valueItem.name, value: [] });
	});
	if (curValue && curValue.length > 0) {
		const { allCheckedNodes, triggerNode, checked } = extra;
		if (props.multiple) {
			allCheckedNodes.forEach((item: IParam) => {
				// 注意 树多选添加选中和删除选中allCheckedNodes的数据结构有差异
				const { value, label, pvalue, plabel } = checked
					? item.node.props
					: item.props;
				const values: string[] = [...pvalue, value];
				const labels: string[] = [...plabel, label];
				valueItemData.forEach((valueItem: IParam, index: number) => {
					valueItem.value.push(values[index]);
				});
				curSelectText.push(labels.join(props.separator));
			});
		} else {
			const { value, label, pvalue, plabel } = triggerNode.props;
			const values: string[] = [...pvalue, value];
			const labels: string[] = [...plabel, label];
			valueItemData.forEach((valueItem: IParam, index: number) => {
				valueItem.value.push(values[index]);
			});
			curSelectText = [labels.join(props.separator)];
		}
	}
	valueItemData.forEach((valueItem: any) => {
		emit('component-action', {
			name: valueItem.name,
			action: 'valueChange',
			data: valueItem.value.length > 0 ? valueItem.value.join(',') : null,
		});
	});
	emit('component-action', {
		name: props.name as string,
		action: 'valueChange',
		data: curSelectText.length > 0 ? JSON.stringify(curSelectText) : null,
	});
};

/**
 * @description 处理加载数据（去重）
 * @param data 加载数据
 * @param pvalue 父数据
 */
const handleLoadData = (data: IParam[], pvalue: string): IParam[] => {
	const tempData: IParam[] = [];
	getTreeData(treeData.value, pvalue, tempData);
	data.forEach((item: IParam) => {
		const _index = tempData.findIndex(
			(_item: any) => item.value == _item.value
		);
		if (_index > 0) {
			Object.assign(tempData[_index], item);
		} else {
			tempData.push(item);
		}
	});
	return tempData;
};

/**
 * @description 级联选择器加载数据
 * @param selectedOptions 选中节点配置
 */
const cascaderLoadData = async (selectedOptions: any) => {
	const targetOption = selectedOptions[selectedOptions.length - 1];
	targetOption.loading = true;
	const { index, value, pvalue, plabel, label } = targetOption;
	const _pvalue = [...pvalue, value];
	const _plabel = [...plabel, label];
	const data: IParam[] = await loadData(
		props.valueItems[index + 1],
		index + 1,
		_pvalue,
		_plabel
	);
	const tempData: IParam[] = handleLoadData(data, value);
	targetOption.loading = false;
	targetOption.children = tempData;
	treeData.value = [...treeData.value];
};

/**
 * @description 树加载数据
 * @param selectedOptions 选中节点配置
 */
const treeLoadData = (selectedOptions: any) => {
	return new Promise(async (resolve) => {
		selectedOptions.loading = true;
		const { index, value, pvalue, plabel, label } = selectedOptions.dataRef;
		const _pvalue = [...pvalue, value];
		const _plabel = [...plabel, label];
		const data = await loadData(
			props.valueItems[index + 1],
			index + 1,
			_pvalue,
			_plabel
		);
		const tempData: IParam[] = handleLoadData(data, value);
		selectedOptions.loading = false;
		selectedOptions.dataRef.children = tempData;
		treeData.value = [...treeData.value];
		resolve(true);
	});
};

/**
 * @description 处理请求参数
 * @param index 树层级索引
 * @param pvalue 父数据值
 */
const handleQueryParams = (index: number, pvalue: string[]) => {
	const context = deepCopy(props.context);
	const viewParams = deepCopy(props.viewParams);
	const entityDetails = App.getAppEntityConfig();
	if (index > 0 && pvalue.length > 0) {
		const pValueItem: ValueItemType = props.valueItems[index - 1];
		const pDataEntity: IEntityDetail | undefined = entityDetails.find(
			(detail: IEntityDetail) => {
				return detail.codeName == pValueItem.appDataEntity;
			}
		);
		if (pDataEntity) {
			Object.assign(context, {
				[pDataEntity.codeName.toLowerCase()]: pvalue[pvalue.length - 1],
			});
			Object.assign(viewParams, {
				[`n_${pDataEntity.keyField.toLowerCase()}_eq`]:
					pvalue[pvalue.length - 1],
			});
		}
	}
	return { context, viewParams };
};

/**
 * @description 加载数据
 * @param valueItem 值项
 * @param index 树层级索引
 * @param pvalue 父数据值集合
 */
const loadData = async (
	valueItem: ValueItemType,
	index: number = 0,
	pvalue: string[] = [],
	plabel: string[] = []
): Promise<IParam[]> => {
	const entityDetails = App.getAppEntityConfig();
	let data: IParam[] = [];
	const valueItemDataEntity: IEntityDetail | undefined = entityDetails.find(
		(detail: IEntityDetail) => {
			return detail.codeName == valueItem.appDataEntity;
		}
	);
	// 未配置属性关联实体 和 数据集
	if (!valueItemDataEntity || !valueItem.dataSet) {
		return data;
	}
	const service = await App.getDataService(
		valueItem.appDataEntity,
		props.context
	);
	// 关联实体服务不存在 不加载数据
	if (!service || isFunc(service[valueItem.dataSet])) {
		return data;
	}
	const { context, viewParams } = handleQueryParams(index, pvalue);
	const response = await service[valueItem.dataSet](context, viewParams);
	if (response.success) {
		data = response.data.map((item: any) => {
			const value = item[valueItemDataEntity.keyField.toLowerCase()];
			const label = item[valueItemDataEntity.majorField.toLowerCase()];
			return {
				value,
				label,
				plabel,
				pvalue,
				isLeaf: index == props.valueItems.length - 1,
				selectable: index == props.valueItems.length - 1,
				disableCheckbox: Object.is(props.editorStyle, 'tree')
					? index != props.valueItems.length - 1
					: false,
				nodeId: `${
					pvalue.length > 0 ? pvalue[pvalue.length - 1] + '_' + value : value
				}`,
				index,
			};
		});
		data.forEach((node: any) => {
			const index = items.findIndex((item: any) => node.value == item.value);
			if (index != -1) {
				items[index] = node;
			} else {
				items.push(node);
			}
		});
	}
	return data;
};
</script>
